﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Aspose.Cells;
using LarasMutiaraDiva.SkripsiLaras.Lib;
using LarasMutiaraDiva.SkripsiLaras.UI.Web.Models;

namespace LarasMutiaraDiva.SkripsiLaras.UI.Web.Controllers
{
    public class EvaluationController : Controller
    {
        
        // GET: /Evaluation/

        public struct RecallPrecision
        {
            public bool  isRelevant { get; set; }
            public float Recall { get; set; }
            public float Precision { get; set; }
        }
        public ActionResult Index()
        {
            

            //InitiateDatabase();

            return View();
        }
        public void EvaluationCalculation()
        {
            var enableEkspansi = Request.Form["enableEkspansi"];
            int numbersOfConcepts = Convert.ToInt32(Request.Form["numbersOfConcepts"]);
            int numbersOfDocuments = Convert.ToInt32(Request.Form["numbersOfDocuments"]);
            int numbersOfPassages = Convert.ToInt32(Request.Form["numbersOfPassages"]);

            Program.NUMBER_OF_TOP_CONCEPTS = numbersOfConcepts;
            Program.NUMBER_OF_TOP_RELEVAN_DOCS = numbersOfDocuments;
            Program.NUMBER_OF_TOP_PASSAGES = numbersOfPassages;

           
            String evaluationTable = string.Empty;
            using (var dc= new KBBIDataContext())
            {
                var listQueries = (from item in dc.RelevanDocuments30Highs
                                   select item.Query).ToList().Distinct();
                
                evaluationTable += "<table border='1' cellpadding='4' cellspacing='0' width='100%' style='font-size: 15px'>";
                evaluationTable += @"<tr>
                                    <th>No</th>
                                    <th>Query</th>
                                    <th>New Query</th>
                                    <th>Num Retrieved Docs</th>
                                    <th>Index of Relevan Docs</th>
                                    <th>0.0</th>
                                    <th>0.1</th>
                                    <th>0.2</th>
                                    <th>0.3</th>
                                    <th>0.4</th>
                                    <th>0.5</th>
                                    <th>0.6</th>
                                    <th>0.7</th>
                                    <th>0.8</th>
                                    <th>0.9</th>
                                    <th>1.0</th>
                                     </tr>
                                   ";
               

                int nomor = 1;
                var AllQueriesPrecision = new List<double[]>();
                var totalOfRetrievedDocs = 0;
                var totalOfRelevantDocExpected = 0;
               
                
                foreach (var query in listQueries)
                {
                    var stemmedQuery = Program.GetStemQuery(query);
                    var newQuery = "-";
                    var docsExspansion = new List<string>();
                    var queryExspansion = string.Empty;
                    var docsResultNoEkspansi = Program.GetTopNDocByQuery(stemmedQuery, int.MaxValue);

                    var docsResult = docsResultNoEkspansi;

                    if (enableEkspansi == "yes")
                    {
                        if (stemmedQuery[stemmedQuery.Count() - 1] == ' ')
                            stemmedQuery = stemmedQuery.Substring(0,stemmedQuery.Count()-1);

                        queryExspansion = Program.GetNewQuery(stemmedQuery);
                        //docsResult = Program.GetTopNDocByQuery(queryExspansion, int.MaxValue);
                       
                        //looking for docsA
                        var docsA=Program.GetTopNDocByQuery(queryExspansion.Replace(" ", " & "), int.MaxValue);
                        //looking for docsB
                        var docsB = Program.GetTopNDocByQuery(stemmedQuery.Replace(" ", " & "), int.MaxValue);

                        //looking for docsC
                        var docsC = Program.GetTopNDocByQuery(stemmedQuery.Replace(" ", " | "), int.MaxValue);

                        //
                        docsResult = docsA.MergeList(docsB, int.MaxValue);
                        docsResult = docsResult.MergeList(docsC, int.MaxValue);
                        
                        newQuery = queryExspansion;

                    }
                    
                    //int N = enableEkspansi.Equals("yes") ? int.MaxValue : docsResultNoEkspansi.Count();
                    //int N = int.MaxValue;
                    //var docsResult = docsExspansion.MergeList(docsResultNoEkspansi,N);

                    totalOfRetrievedDocs += docsResult.Count;
                    //generate list of RecallPrecision for each query
                        var listRecallPrecision = new List<RecallPrecision>();
                        var numberOfRelevan = 0;
                        var numberOfRetrieved = 0;
                        var indexOfRelevan = 1;
                        var sumOfRelevanIndex = 0;
                        var indexOfRelevanDocs = string.Empty;
                        var numberOfRelevantDocExpected = dc.RelevanDocuments.Where(i => i.Query == query).Count();
                    totalOfRelevantDocExpected += numberOfRelevantDocExpected;
                        foreach (var d in docsResult)
                        {
                            numberOfRetrieved++;

                            bool relevan = false;
                            //float recall = 0;
                            //float precision = 0;
                            //
                            if(query.Equals("Bencana kekeringan"))
                            {
                                var temp0 = from i in dc.RelevanDocuments
                                            where i.Docno.Replace('-', ' ').Trim() == d.Trim()
                                            select i.Docno;
                            }


                            var temp = dc.RelevanDocuments.Where(
                                    i => i.Query == query && 
                                    i.Docno.Replace('-',' ').Trim() == d.Trim()
                                ).SingleOrDefault();

                            //relevan = temp == null ? false : true;
                            if (temp != null)
                                relevan = true;

                            if (relevan)
                            {
                                numberOfRelevan++;
                                indexOfRelevanDocs += Convert.ToString(indexOfRelevan) + ",";
                                sumOfRelevanIndex += indexOfRelevan;
                            }
                            //
                            listRecallPrecision.Add(
                                new RecallPrecision()
                                    {
                                        isRelevant = relevan,
                                        Recall = (float) numberOfRelevan / numberOfRelevantDocExpected,
                                        Precision = (float) numberOfRelevan/numberOfRetrieved
                                    }
                                
                                );
                            //
                            indexOfRelevan++;
                        }

                    //end
                    var precison = new double [11];
                    int index = 0;
                    for (double i = 0.0; i <= 1.0; i+=0.1)
                    {
                        precison[index++] = GetPrecisionValue2(listRecallPrecision, i);
                    }
                    evaluationTable += "<tr>";
                        //evaluationTable += "<tr>";
                            
                            evaluationTable += string.Format("<td>{0}</td>", nomor++);
                            evaluationTable += string.Format("<td>{0}</td>", query);
                            evaluationTable += string.Format("<td>{0}</td>", newQuery);
                            evaluationTable += string.Format("<td>{0} | {1}</td>", docsResult.Count, numberOfRelevantDocExpected);
                            evaluationTable += string.Format("<td>({0}) : {1} <h2>{2}</h2></td>",numberOfRelevan, indexOfRelevanDocs, sumOfRelevanIndex);
                                for (int i = 0; i < precison.Count(); i++)
                                        evaluationTable += string.Format("<td>{0}</td>", (float) precison[i]);
                    //evaluationTable += "</tr>";
                    evaluationTable += "</tr>";
                    AllQueriesPrecision.Add(precison);
                  
                    
                }
                evaluationTable += "<tr>";
                    evaluationTable += string.Format("<td colspan='3'>{0}</td>", "RATA-RATA");
                    double total = 0;
                    evaluationTable += string.Format("<td >{0}|{1}</td>", totalOfRetrievedDocs, totalOfRelevantDocExpected);
                    evaluationTable += "<td>-</td>";
                    for (int i = 0; i < 11; i++)
                    {
                        var temp = GetPrecisionAverage(AllQueriesPrecision, i);
                        total += temp;
                        evaluationTable += string.Format("<td >{0}</td>",(float)  temp);
                    }

                evaluationTable += "</tr>";
                evaluationTable += "</table>";
                evaluationTable += string.Format("<br /><h2>AVP: {0}</h2>", total/11);

            }
            Response.Write(evaluationTable);
        }
        public double GetPrecisionAverage(List<double[]> list, int index)
        {
            return (from i in list
                       select i[index]).Average();
        }
        
        public float GetPrecisionValue(List<RecallPrecision> listRecallPrecision, double recall)
        {
            var temp = (from i in listRecallPrecision
                        where i.Recall >= recall
                        select i.Precision).ToList();

            if (temp.Count > 0)
                return temp.Max();


            var temp2 = (from i in listRecallPrecision
                         where i.isRelevant
                         select i.Precision).ToList();

            if (temp2.Count > 0)
                return temp2.Last();

            return (from i in listRecallPrecision
                    //where i.isRelevant
                    select i.Precision).ToList().Last();
        }

        public float GetPrecisionValue2(List<RecallPrecision> listRecallPrecision, double recall)
        {
            var temp = (from i in listRecallPrecision
                        where i.Recall >= recall
                        select i.Precision).ToList();

            if (temp.Count > 0)
                return temp.Max();
            else
                return 0;

            /*
            var temp2 = (from i in listRecallPrecision
                         where i.isRelevant
                         select i.Precision).ToList();

            if (temp2.Count > 0)
                return temp2.Last();

            return (from i in listRecallPrecision
                    //where i.isRelevant
                    select i.Precision).ToList().Last();
             */
        }
        
        public void InitiateDatabase()
        {
            int rows = 0;
            var data = GetExcelDataUsingAspose(@"C:\query-and-doc-relevant.xls", out rows);

            var list = new List<RelevanDocument>();

            for (int i = 0; i < rows; i++)
            {
                string query = (string) data[i, 0];
                string textDocs = (string) data[i, 1];
                var arrayDocNo = textDocs.Split(',');
                for (int j = 0; j < arrayDocNo.Length; j++)
                {
                    list.Add(
                        new RelevanDocument()
                            {
                                Docno = arrayDocNo[j],
                                Query = query
                           }
                        );
                }
            }

            using (var dc=new KBBIDataContext())
            {
                dc.ObjectTrackingEnabled = true;
                dc.RelevanDocuments.InsertAllOnSubmit(list);
                dc.SubmitChanges();
            }
        }
        public static object[,] GetExcelDataUsingAspose(string location, out int numbersOfRows)
        {
            //Creating a file stream containing the Excel file to be opened
            FileStream fstream = new FileStream(location, FileMode.Open);

            //Instantiating a Workbook object
            //Opening the Excel file through the file stream

            Workbook workbook = new Workbook();
            workbook.Open(fstream);

            Worksheet worksheet = workbook.Worksheets[1];

            int rows = worksheet.Cells.MaxDataRow + 1;
            int cols = worksheet.Cells.MaxDataColumn;
            numbersOfRows = rows;
            var arrayData = worksheet.Cells.ExportArray(0, 0, rows, 2);


            //Closing the file stream to free all resources
            fstream.Close();
            return arrayData;
        }

    }
}
